package top.bug.blog.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.upyun.UpException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import top.bug.blog.constant.SourceConstants;
import top.bug.blog.entity.dto.common.BatchRequest;
import top.bug.blog.entity.dto.common.SingleRequest;
import top.bug.blog.entity.dto.source.*;
import top.bug.blog.entity.po.Source;
import top.bug.blog.entity.po.SourceGroup;
import top.bug.blog.entity.vo.source.SourceCountVO;
import top.bug.blog.enums.ErrorCode;
import top.bug.blog.exception.BussinessException;
import top.bug.blog.mapper.SourceGroupMapper;
import top.bug.blog.mapper.SourceMapper;
import top.bug.blog.service.SourceService;
import top.bug.blog.utils.QueryWrapperUtils;
import top.bug.blog.utils.UpYunUtil;

import javax.annotation.Resource;
import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

import static top.bug.blog.utils.StrUtils.isNotBlank;

/**
 * @author <a href="https://github.com/jiusi66">九思.</a>
 * @email 10837907@qq.com
 * @from <a href="https://www.52bug.top">递归茶馆-www.52bug.top</a>
 * @description 素材 实现类
 */
@Service
public class SourceServiceImpl extends ServiceImpl<SourceMapper, Source> implements SourceService {
    @Resource
    private SourceMapper sourceMapper;
    @Resource
    private SourceGroupMapper sourceGroupMapper;

    /**
     * 所有常见图片后缀
     */
    private static final List<String> IMAGE_SUFFIX = Arrays.asList("jpg", "jpeg", "png", "gif", "bmp", "webp");
    /**
     * 枚举所有常见文档后缀
     */
    private static final List<String> DOC_SUFFIX = Arrays.asList("doc", "docx", "xls", "xlsx", "ppt", "pptx", "pdf", "md");

    // region 素材相关

    @Override
    public Page<Source> getList(SourceQueryRequest queryRequest) {
        if (null == queryRequest) {
            throw new BussinessException(ErrorCode.PARAM_NOT_COMPLETE);
        }
        Page<Source> page = new Page<>(queryRequest.getPageNum(), queryRequest.getPageSize());
        String name = queryRequest.getName();
        String sortField = queryRequest.getSortField();
        String groupId = queryRequest.getGroupId();
        String fileType = queryRequest.getFileType();
        LambdaQueryWrapper<Source> queryWrapper = new LambdaQueryWrapper<Source>()
                .like(isNotBlank(name), Source::getName, name)
                .eq(isNotBlank(groupId), Source::getGroupId, groupId)
                .eq(isNotBlank(fileType), Source::getFileType, fileType);
        // 时间范围参数
        QueryWrapperUtils.addTimeRangeCondition(queryWrapper, queryRequest.getCreateTimeRange(), Source::getCreateOn);
        QueryWrapperUtils.addTimeRangeCondition(queryWrapper, queryRequest.getUpdateTimeRange(), Source::getUpdateOn);
        // 定义允许排序的字段白名单
        Map<String, SFunction<Source, ?>> sortFields = new HashMap<>(3);
        sortFields.put("name", Source::getName);
        sortFields.put("updateOn", Source::getUpdateOn);
        sortFields.put("fileSize", Source::getFileSize);
        // 添加安全排序
        QueryWrapperUtils.addSafeSort(queryWrapper, sortField, queryRequest.getSortOrder(), sortFields);
        sourceMapper.selectPage(page, queryWrapper);
        return page;
    }

    @Override
    public SourceCountVO getSourceCount() {
        // 获取全部数量
        List<SourceCountDTO> list = sourceMapper.selectFileTypeCount();
        Long imageCount = 0L;
        Long docCount = 0L;
        Long otherCount = 0L;
        // 分别统计图片、文档、其他类型素材数量
        for (SourceCountDTO dto : list) {
            String fileType = dto.getFileType();
            Long count = dto.getCount();
            switch (fileType) {
                case SourceConstants.FILE_TYPE_IMAGES:
                    imageCount += count;
                    break;
                case SourceConstants.FILE_TYPE_DOCS:
                    docCount += count;
                    break;
                default:
                    otherCount += count;
                    break;
            }
        }
        SourceCountVO sourceCountVO = new SourceCountVO();
        sourceCountVO.setImageCount(imageCount);
        sourceCountVO.setDocCount(docCount);
        sourceCountVO.setOtherCount(otherCount);
        return sourceCountVO;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public List<String> uploadSource(SourceUploadRequest uploadRequest) {
        if (null == uploadRequest) {
            throw new BussinessException(ErrorCode.PARAM_NOT_COMPLETE);
        }
        List<MultipartFile> files = uploadRequest.getFiles();
        String groupId = uploadRequest.getGroupId();
        String path = SourceConstants.PATH_PREFIX;
        ArrayList<String> res = new ArrayList<>();
        if (files.isEmpty() || StrUtil.isEmpty(groupId)) {
            return res;
        }
        for (MultipartFile file : files) {
            String fileName = file.getOriginalFilename();
            long size = file.getSize();
            // 查询图片名数量
            Long count = sourceMapper.selectCount(new LambdaQueryWrapper<Source>()
                    .eq(Source::getName, fileName)
            );
            if (count > 0) {
                // 临时获取后缀
                String suffix = fileName.substring(fileName.lastIndexOf(".") + 1);
                // 后缀之前
                fileName = fileName.substring(0, fileName.lastIndexOf(".")) + "(" + (count - 1) + ")";
                fileName = fileName + "." + suffix;
            }
            // 文件上传
            String url = "";
            try {
                url = UpYunUtil.upload(file, path);
                res.add(url);
            } catch (Exception e) {
                throw new BussinessException(ErrorCode.FILE_UPLOAD_ERROR);
            }
            // 插入数据库
            Source sourcePo = new Source();
            sourcePo.setName(fileName);
            sourcePo.setFileType(getFileType(fileName));
            sourcePo.setUrl(url);
            sourcePo.setFileName(url.substring(url.lastIndexOf("/") + 1));
            sourcePo.setFileSize(size);
            sourcePo.setGroupId(groupId);
            sourceMapper.insert(sourcePo);
        }
        // 更新组别中的文件数量
        sourceGroupMapper.modFileCount(groupId, res.size());
        return res;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean batchDelete(BatchRequest batchRequest) {
        if (null == batchRequest) {
            throw new BussinessException(ErrorCode.PARAM_NOT_COMPLETE);
        }
        List<String> ids = batchRequest.getIds();
        // 获取被删除文件的文件名
        List<Source> sourcePoList = sourceMapper.selectBatchIds(ids);
        // 删除文件
        boolean delete = false;
        int delCount = 0;
        for (Source source : sourcePoList) {
            String name = source.getFileName();
            String id = source.getId();
            try {
                delete = UpYunUtil.delete(SourceConstants.PATH_PREFIX + "/" + name);
            } catch (IOException | UpException e) {
                throw new RuntimeException(e);
            }
            if (!delete) {
                break;
            }
            delCount += sourceMapper.deleteById(id);
        }
        if (!delete) {
            throw new BussinessException(ErrorCode.FILE_DELETE_ERROR);
        }
        // 删除表记录
        return ids.size() == delCount;
    }

    // endregion

    // region 素材分组

    @Override
    public Boolean createGroup(SourceGroupAddRequest addRequest) {
        if (null == addRequest) {
            throw new BussinessException(ErrorCode.PARAM_NOT_COMPLETE);
        }
        String name = addRequest.getName();
        if (StrUtil.isBlank(name)) {
            throw new BussinessException(ErrorCode.PARAM_NOT_COMPLETE);
        }
        // 创建分组
        SourceGroup group = new SourceGroup();
        BeanUtil.copyProperties(addRequest, group);
        return sourceGroupMapper.insert(group) > 0;
    }

    @Override
    public Boolean deleteGroup(SingleRequest singleRequest) {
        if (null == singleRequest) {
            throw new BussinessException(ErrorCode.PARAM_NOT_COMPLETE);
        }
        String id = singleRequest.getId();
        if (StrUtil.isBlank(id)) {
            throw new BussinessException(ErrorCode.PARAM_NOT_COMPLETE);
        }
        // 移动当前分组下的数据到未分组（-1）
        List<String> ids = sourceMapper.selectList(new LambdaQueryWrapper<Source>()
                .eq(Source::getGroupId, id)).stream().map(Source::getId).collect(Collectors.toList());
        if (CollUtil.isNotEmpty(ids)) {
            sourceMapper.update(new LambdaUpdateWrapper<Source>()
                    .set(Source::getGroupId, SourceConstants.DEFAULT_GROUP_ID)
                    .in(Source::getId, ids)
            );
        }
        // 删除当前分组
        return sourceGroupMapper.deleteById(id) > 0;
    }

    @Override
    public Boolean updateGroup(SourceGroupUpdateRequest updateRequest) {
        if (updateRequest == null || updateRequest.getId() == null) {
            throw new BussinessException(ErrorCode.PARAM_NOT_COMPLETE);
        }
        // 分组名称不能重复
        Long count = sourceGroupMapper.selectCount(new LambdaQueryWrapper<SourceGroup>()
                .eq(SourceGroup::getName, updateRequest.getName())
                .ne(SourceGroup::getId, updateRequest.getId())
        );
        if (count > 0) {
            throw new BussinessException(ErrorCode.DATA_ALREADY_EXISTED);
        }
        SourceGroup sourceGroup = new SourceGroup();
        BeanUtil.copyProperties(updateRequest, sourceGroup);
        return sourceGroupMapper.updateById(sourceGroup) > 0;
    }

    @Override
    public List<SourceGroup> getGroups() {
        return sourceGroupMapper.selectList(null);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean batchMoveToGroup(SourceMoveRequest moveRequest) {
        if (null == moveRequest) {
            throw new BussinessException(ErrorCode.PARAM_NOT_COMPLETE);
        }
        List<String> ids = moveRequest.getIds();
        String groupId = moveRequest.getGroupId();
        if (CollUtil.isEmpty(ids) || StrUtil.isEmpty(groupId)) {
            return false;
        }
        // 批量更新
        boolean flag = sourceMapper.update(new LambdaUpdateWrapper<Source>()
                .set(Source::getGroupId, groupId)
                .in(Source::getId, ids)
        ) > 0;
        // 更新组别中的文件数量
        sourceGroupMapper.modFileCount(groupId, ids.size());
        return flag;
    }

    // endregion

    // region 私有方法

    /**
     * 获取文件类型，映射为 图片-0、文档-1、其他-2
     */
    private String getFileType(String fileName) {
        if (StrUtil.isBlank(fileName)) {
            throw new BussinessException(ErrorCode.DATA_IS_WRONG);
        }
        String suffix = fileName.substring(fileName.lastIndexOf(".") + 1);
        if (IMAGE_SUFFIX.contains(suffix)) {
            return SourceConstants.FILE_TYPE_IMAGES;
        } else if (DOC_SUFFIX.contains(suffix)) {
            return SourceConstants.FILE_TYPE_DOCS;
        } else {
            return SourceConstants.FILE_TYPE_OTHER;
        }
    }

    // endregion
}
